#include "ThreadSimpleSyncDemoOfCpp.h"

#include <list>
#include <iostream>
#include <sstream>
#include <functional>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>

using namespace std;

int thread_common_number_cpp = 0;
mutex mutexLock;

void cumulativeCountOfCpp()
{
    for (int index = 0; index < 50; index++)
    {
        mutexLock.lock();
        int number = thread_common_number_cpp;
        this_thread::sleep_for(chrono::milliseconds(rand() % 50));
        number++;
        thread_common_number_cpp = number;
        cout << to_string(thread_common_number_cpp) + "\n";
        mutexLock.unlock();
    }
}

void threadSimpleSyncDemoOfCpp()
{
    cout << "最简多线程同步使用案例（基于 C++ 标准库）开始演示" << endl;
    thread_common_number_cpp = 0;

    // 如同 C 多线程库一样，只要涉及多线程，那么都会有同步的问题，因为系统在设计时间片分配时就不可能做到有序，某些因为时间片到期需要放弃时间片，
    // 某些因为需要进行IO操作需要放弃时间片，某些一直在等待时间片，某些可能还在进行 IO 操作不希望马上得到时间片。这些都不是可以提前规划的。
    // 而且更多的是大量需要获取时间片的进程在争抢执行机会。所以时间片是随机的分配的。

    // 解决多线程数据混乱的方案就是进行线程同步，最常用的就是互斥锁，在 C++11 中一共提供了四种互斥锁：
    //  * std::mutex：独占的互斥锁，不能递归使用
    //  * std::timed_mutex：带超时的独占互斥锁，不能递归使用
    //  * std::recursive_mutex：递归互斥锁，不带超时功能
    //  * std::recursive_timed_mutex：带超时的递归互斥锁
    // 互斥锁在有些资料中也被称之为互斥量，二者是一个东西。

    // 1. std::mutex
    // 不论是在 C 还是 C++ 中，进行线程同步的处理流程基本上是一致的，C++ 的 mutex 类提供了相关的 API 函数：

    // 成员函数：
    //  1. lock(): 函数用于给临界区加锁，并且只能有一个线程获得锁的所有权，它有阻塞线程的作用，函数原型如下：
    //
    //     - void lock();
    //
    // 独占互斥锁对象有两种状态：锁定和未锁定。
    //  * 如果互斥锁是打开的，调用 lock() 函数的线程会得到互斥锁的所有权，并将其上锁，其它线程再调用该函数的时候由于得不到互斥锁的所有权，就会被 lock() 函数阻塞。
    //  * 当拥有互斥锁所有权的线程将互斥锁解锁，此时被 lock() 阻塞的线程解除阻塞，抢到互斥锁所有权的线程加锁并继续运行，没抢到互斥锁所有权的线程继续阻塞。
    //
    // 2. try_lock()：除了使用 lock() 还可以使用 try_lock() 获取互斥锁的所有权并对互斥锁加锁，函数原型如下：
    //
    //    - bool try_lock();
    //
    // 二者的区别在于 try_lock() 不会阻塞线程，lock() 会阻塞线程：
    //  * 如果互斥锁是未锁定状态，得到了互斥锁所有权并加锁成功，函数返回 true
    //  * 如果互斥锁是锁定状态，无法得到互斥锁所有权加锁失败，函数返回 false
    //
    // 3. unlock()：当互斥锁被锁定之后可以通过 unlock() 进行解锁，该函数的函数原型如下：
    //
    //    - void unlock();
    //
    // 需要注意的是只有拥有互斥锁所有权的线程也就是对互斥锁上锁的线程才能将其解锁，其它线程是没有权限做这件事情的。

    // 通过介绍以上三个函数，使用互斥锁进行线程同步的大致思路差不多就能搞清楚了，和 C 的线程库使用步骤一样,主要分为以下几步：
    //  * 找到多个线程操作的共享资源（全局变量、堆内存、类成员变量等），也可以称之为临界资源；
    //  * 找到和共享资源有关的上下文代码，也就是临界区（下图中的黄色代码部分）；
    //  * 在临界区的上边调用互斥锁类的 lock() 方法；
    //  * 在临界区的下边调用互斥锁的 unlock() 方法。
    //
    // 线程同步的目的是让多线程按照顺序依次执行临界区代码，这样做线程对共享资源的访问就从并行访问变为了线性访问，访问效率降低了，但是保证了数据的正确性。
    // 当线程对互斥锁对象加锁，并且执行完临界区代码之后，一定要使用这个线程对互斥锁解锁，否则最终会造成线程的死锁。
    // 死锁之后当前应用程序中的所有线程都会被阻塞，并且阻塞无法解除，应用程序也无法继续运行。

    // 使用示例：
    // 举个栗子，我们让两个线程共同操作同一个全局变量，二者交替数数，将数值存储到这个全局变量里边并打印出来。

    thread cumulativeCountThread1(cumulativeCountOfCpp);
    thread cumulativeCountThread2(cumulativeCountOfCpp);

    cumulativeCountThread1.join();
    cumulativeCountThread2.join();

    // 在上面的示例程序中，两个子线程执行的任务的一样的（其实也可以不一样，不同的任务中也可以对共享资源进行读写操作），
    // 在任务函数中把与全局变量相关的代码加了锁，两个线程只能顺序访问这部分代码（如果不进行线程同步打印出的数据是混乱且无序的）。
    // 另外需要强调注意的：
    //  * 在所有线程的任务函数执行完毕之前，互斥锁对象是不能被析构的，一定要在程序中保证这个对象的可用性。
    //  * 互斥锁的个数和共享资源的个数相等，也就是说每一个共享资源都应该对应一个互斥锁对象。互斥锁对象的个数和线程的个数没有关系。

    // 2. std::lock_guard
    // lock_guard 是 C++11 新增的一个模板类，使用这个类，可以简化互斥锁 lock() 和 unlock() 的写法，同时也更安全。
    // 这个模板类的定义和常用的构造函数原型如下：
    //    类的定义，定义于头文件 <mutex>
    //    template<class Mutex>
    //    class lock_guard;
    //    常用构造函数
    //    explicit lock_guard(mutex_type& m);
    // lock_guard 在使用上面提供的这个构造函数构造对象时，会自动锁定互斥量，而在退出作用域后进行析构时就会自动解锁，从而保证了互斥量的正确操作，避免忘记 unlock() 操作而导致线程死锁。
    // lock_guard 使用了 RAII 技术，就是在类构造函数中分配资源，在析构函数中释放资源，保证资源出了作用域就释放。
    //
    // summer: RAII 技术，是一种资源管理技术，利用 C++ 对象生命周期来对资源进行管理，在对象构造函数创建时申请资源，析构函数调用时释放资源，
    //         使得资源的生命周期与对象同步，这样来避免资源忘记释放的问题。所以上面的方法可以让模板对象构造时加锁析构时解锁，由于声明的局部栈对象在超出范围后会自动释放对象解锁。
    //         这样做显然不能使用指针来声明对象，否则还是需要自己手动删除对象来解锁。

    // 使用lock_guard对上面的例子进行修改，代码如下：
    void slow_increment(int id);
    for (int index = 0; index < 3; index++)
    {
        thread slow_increment_thread(slow_increment, index);
        slow_increment_thread.detach();
    }

    this_thread::sleep_for(chrono::seconds(9));

    // 通过修改发现代码被精简了，而且不用担心因为忘记解锁而造成程序的死锁，但是这种方式也有弊端，在上面的示例程序中整个 for 循环的体都被当做了临界区，
    // 多个线程是线性的执行临界区代码的，因此临界区越大程序效率越低，还是需要根据实际情况选择最优的解决方案。

    // 3. std::recursive_mutex
    // 递归互斥锁 std::recursive_mutex 允许同一线程多次获得互斥锁，可以用来解决同一线程需要多次获取互斥量时死锁的问题。
    // 在下面的例子中使用独占非递归互斥量会发生死锁：
    /*
    struct Calculate
    {
        Calculate() : m_i(6) {}
        void mul(int x)
        {
            lock_guard<mutex> locker(m_mutex);
            m_i *= x;
            cout << "m_i::" << m_i << endl;
        }

        void div(int x)
        {
            lock_guard<mutex> locker(m_mutex);
            m_i /= x;
            cout << "m_i::" << m_i << endl;
        }

        void both(int x, int y)
        {
            lock_guard<mutex> locker(m_mutex);
            mul(x);
            div(y);
        }

        int m_i;
        mutex m_mutex;
    };
    Calculate cal;
    cal.both(6, 3);
    */
    // 上面的程序中执行了 cal.both(6, 3); 调用之后，程序就会发生死锁，在 both() 中开始就已经对 m_mutex 互斥锁加锁了，继续调用 mul() 函数，会试图再次对 m_mutex 加锁。
    // 已经得到互斥锁所有权的线程再次获取这个互斥锁的所有权就会造成死锁（在 C++ 中程序会异常退出，使用 C 库函数会导致这个互斥锁永远无法被解锁，最终阻塞所有的线程）。
    // 要解决这个死锁的问题，一个简单的办法就是使用递归互斥锁 std::recursive_mutex，它允许一个线程多次获得互斥锁的所有权。
    // 修改之后的代码如下：
    struct Calculate
    {
        Calculate() : m_i(6) {}
        void mul(int x)
        {
            lock_guard<recursive_mutex> locker(m_mutex);
            m_i *= x;
            cout << "m_i::" << m_i << endl;
        }

        void div(int x)
        {
            lock_guard<recursive_mutex> locker(m_mutex);
            m_i /= x;
            cout << "m_i::" << m_i << endl;
        }

        void both(int x, int y)
        {
            lock_guard<recursive_mutex> locker(m_mutex);
            mul(x);
            div(y);
        }

        int m_i;
        recursive_mutex m_mutex;
    };
    Calculate cal;
    cal.both(6, 3);
    // 虽然递归互斥锁可以解决同一个互斥锁频繁获取互斥锁资源的问题，但是还是建议少用，主要原因如下：
    //  * 使用递归互斥锁的场景往往都是可以简化的，使用递归互斥锁很容易放纵复杂逻辑的产生，从而导致 bug 的产生。
    //  * 递归互斥锁比非递归互斥锁效率要低一些。
    //  * 递归互斥锁虽然允许同一个线程多次获得同一个互斥锁的所有权，但最大次数并未具体说明，一旦超过一定的次数，就会抛出 std::system 错误。

    // 4. std::timed_mutex
    // std::timed_mutex 是超时独占互斥锁，主要是在获取互斥锁资源时增加了超时等待功能，
    // 因为不知道获取锁资源需要等待多长时间，为了保证不一直等待下去，设置了一个超时时长，超时后线程就可以解除阻塞去做其他事情了。
    //
    // std::timed_mutex 比 std::_mutex 多了两个成员函数：try_lock_for() 和 try_lock_until()：
    // 相关函数原型：
    /*
    void lock();
    bool try_lock();
    void unlock();

    // std::timed_mutex比std::_mutex多出的两个成员函数
    template <class Rep, class Period>
    bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);

    template <class Clock, class Duration>
    bool try_lock_until (const chrono::time_point<Clock,Duration>& abs_time);
    */
    //  * try_lock_for    函数是当线程获取不到互斥锁资源的时候，让线程阻塞一定的时间长度
    //  * try_lock_until  函数是当线程获取不到互斥锁资源的时候，让线程阻塞到某一个指定的时间点
    //  * 关于两个函数的返回值：
    //    - 当得到互斥锁的所有权之后，函数会马上解除阻塞，返回 true，
    //    - 如果阻塞的时长用完或者到达指定的时间点之后，函数也会解除阻塞，返回 false
    //
    // 下面的示例程序中为大家演示了 std::timed_mutex 的使用：
    void timed_mutex_work();
    thread timed_mutex_thread1(timed_mutex_work);
    thread timed_mutex_thread2(timed_mutex_work);

    timed_mutex_thread1.join();
    timed_mutex_thread2.join();

    // 在上面的例子中，通过一个 while 循环不停的去获取超时互斥锁的所有权，如果得不到就阻塞 1 秒钟，
    // 1 秒之后如果还是得不到阻塞 50 毫秒，然后再次继续尝试，直到获得互斥锁的所有权，跳出循环体。

    // 关于递归超时互斥锁 std::recursive_timed_mutex 的使用方式和 std::timed_mutex 是一样的，只不过它可以允许一个线程多次获得互斥锁所有权，
    // 而 std::timed_mutex 只允许线程获取一次互斥锁所有权。另外，递归超时互斥锁 std::recursive_timed_mutex 也拥有和 std::recursive_mutex 一样的弊端，不建议频繁使用。

    //*************************************************************************************************************************************************************
    // C++ 线程同步：条件变量
    // 条件变量是 C++11 提供的另外一种用于等待的同步机制，它能阻塞一个或多个线程，直到收到另外一个线程发出的通知或者超时时，才会唤醒当前阻塞的线程。
    // 条件变量需要和互斥量配合起来使用，C++11 提供了两种条件变量：
    //  * condition_variable：需要配合 std::unique_lock<std::mutex> 进行 wait 操作，也就是阻塞线程的操作。
    //  * condition_variable_any：可以和任意带有 lock()、unlock() 语义的 mutex 搭配使用，也就是说有四种：
    //     - std::mutex：独占的非递归互斥锁
    //     - std::timed_mutex：带超时的独占非递归互斥锁
    //     - std::recursive_mutex：不带超时功能的递归互斥锁
    //     - std::recursive_timed_mutex：带超时的递归互斥锁
    //
    // 条件变量通常用于生产者和消费者模型，大致使用过程如下：
    //  * 拥有条件变量的线程获取互斥量;
    //  * 循环检查某个条件，如果条件不满足阻塞当前线程，否则线程继续向下执行:
    //     - 产品的数量达到上限，生产者阻塞，否则生产者一直生产。。。
    //     - 产品的数量为零，消费者阻塞，否则消费者一直消费。。。
    //  * 条件满足之后，可以调用 notify_one() 或者 notify_all() 唤醒一个或者所有被阻塞的线程:
    //     - 由消费者唤醒被阻塞的生产者，生产者解除阻塞继续生产。。。
    //     - 由生产者唤醒被阻塞的消费者，消费者解除阻塞继续消费。。。
    //
    // 1. condition_variable
    //
    // 成员函数
    // condition_variable 的成员函数主要分为两部分：
    //  * 线程等待（阻塞）函数;
    //  * 线程通知（唤醒）函数;
    // 这些函数被定义于头文件 <condition_variable>。
    //
    // 等待函数:
    // wait() 调用该函数的线程会被阻塞
    // 函数原型一
    //  void wait (unique_lock<mutex>& lck);
    // 函数原型二
    //  template <class Predicate>
    //  void wait (unique_lock<mutex>& lck, Predicate pred);
    //
    //  * 函数原型一：调用该函数的线程直接被阻塞；
    //  * 函数原型二：该函数的第二个参数是一个判断条件，是一个返回值为布尔类型的函数：
    //     - 该参数可以传递一个有名函数的地址，也可以直接指定一个匿名函数;
    //     - 表达式返回 false 当前线程被阻塞，表达式返回 true 当前线程不会被阻塞，继续向下执行。
    //  * 独占的互斥锁对象不能直接传递给 wait() 函数，需要通过模板类 unique_lock 进行二次处理，通过得到的对象仍然可以对独占的互斥锁对象做如下操作，使用起来更灵活。
    //
    //    |   公共成员函数   |                   说明
    //    |      lock       |  锁定关联的互斥锁
    //    |    try_lock     |  尝试锁定关联的互斥锁，若无法锁定，函数直接返回
    //    |  try_lock_for   |  试图锁定关联的可定时锁定互斥锁，若互斥锁在给定时长中仍不能被锁定，函数返回
    //    | try_lock_until  |  试图锁定关联的可定时锁定互斥锁，若互斥锁在给定的时间点后仍不能被锁定，函数返回
    //    |     unlock      |  将互斥锁解锁
    //
    //  * 如果线程被该函数阻塞，这个线程会释放占有的互斥锁的所有权，当阻塞解除之后这个线程会重新得到互斥锁的所有权，
    //    继续向下执行（这个过程是在函数内部完成的，了解这个过程即可，其目的是为了避免线程的死锁）。
    //
    //
    // wait_for() 函数和 wait() 的功能是一样的，只不过多了一个阻塞时长，假设阻塞的线程没有被其他线程唤醒，当阻塞时长用完之后，线程就会自动解除阻塞，继续向下执行。
    // 函数原型
    //  template <class Rep, class Period>
    //  cv_status wait_for(unique_lock<mutex>& lck, const chrono::duration<Rep,Period>& rel_time);
    //
    //  template <class Rep, class Period, class Predicate>
    //  bool wait_for(unique_lock<mutex>& lck, const chrono::duration<Rep,Period>& rel_time, Predicate pred);
    //
    //
    // wait_until() 函数和 wait_for() 的功能是一样的，它是指定让线程阻塞到某一个时间点，假设阻塞的线程没有被其他线程唤醒，当到达指定的时间点之后，线程就会自动解除阻塞，继续向下执行。
    // 函数原型
    //  template <class Clock, class Duration>
    //  cv_status wait_until(unique_lock<mutex>& lck, const chrono::time_point<Clock,Duration>& abs_time);
    //
    //  template <class Clock, class Duration, class Predicate>
    //  bool wait_until(unique_lock<mutex>& lck, const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);
    //
    //
    //
    // 通知函数
    // 函数原型
    //  void notify_one() noexcept;
    //  void notify_all() noexcept;
    //
    //  * notify_one()：唤醒一个被当前条件变量阻塞的线程
    //  * notify_all()：唤醒全部被当前条件变量阻塞的线程

    // 生产者和消费者模型
    void syncQueueDemo();
    syncQueueDemo();


    // 2. condition_variable_any
    // 
    // 成员函数
    // condition_variable_any 的成员函数也是分为两部分：
    //  * 线程等待（阻塞）函数；
    //  * 线程通知（唤醒）函数；
    // 这些函数被定义于头文件 <condition_variable>。
    // 
    // 等待函数:
    // wait() 调用该函数的线程会被阻塞
    // 函数原型一
    //  template<class Lock> 
    //  void wait(Lock& lck);
    // 函数原型二
    //  template <class Lock, class Predicate>
    //  void wait (Lock& lck, Predicate pred);
    // 
    //  * 函数原型一：调用该函数的线程直接被阻塞；
    //  * 函数原型二：该函数的第二个参数是一个判断条件，是一个返回值为布尔类型的函数：
    //     - 该参数可以传递一个有名函数的地址，也可以直接指定一个匿名函数;
    //     - 表达式返回 false 当前线程被阻塞，表达式返回 true 当前线程不会被阻塞，继续向下执行。
    //  * 可以直接传递给 wait() 函数的互斥锁类型有四种，分别是:
    //     - std::mutex；
    //     - std::timed_mutex；
    //     - std::recursive_mutex；
    //     - std::recursive_timed_mutex。
    //  * 如果线程被该函数阻塞，这个线程会释放占有的互斥锁的所有权，
    //    当阻塞解除之后这个线程会重新得到互斥锁的所有权，继续向下执行（这个过程是在函数内部完成的，了解这个过程即可，其目的是为了避免线程的死锁）。
    //
    // wait_for() 函数和 wait() 的功能是一样的，只不过多了一个阻塞时长，假设阻塞的线程没有被其他线程唤醒，当阻塞时长用完之后，线程就会自动解除阻塞，继续向下执行。
    // 函数原型
    //   template<class Lock, class Rep, class Period>
    //   cv_status wait_for(Lock& lck, const chrono::duration<Rep, Period>& rel_time);
	//
    //   template<class Lock, class Rep, class Period, class Predicate>
    //   bool wait_for(Lock& lck, const chrono::duration<Rep, Period>& rel_time, Predicate pred);
    //
    //
    // wait_until() 函数和 wait_for() 的功能是一样的，它是指定让线程阻塞到某一个时间点，假设阻塞的线程没有被其他线程唤醒，当到达指定的时间点之后，线程就会自动解除阻塞，继续向下执行。
    // 函数原型
    //   template<class Lock, class Clock, class Duration>
    //   cv_status wait_until(Lock& lck, const chrono::time_point<Clock, Duration>& abs_time);
    // 
    //   template<class Lock, class Clock, class Duration, class Predicate>
    //   bool wait_until(Lock& lck, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);
    //
    //
    // 通知函数
    // 函数原型
    //   void notify_one() noexcept;
    //   void notify_all() noexcept;
    //
    //  * notify_one()：唤醒一个被当前条件变量阻塞的线程
    //  * notify_all()：唤醒全部被当前条件变量阻塞的线程

    // 生产者和消费者模型
    // 使用条件变量 condition_variable_any 同样可以实现上面的生产者和消费者的例子，代码只有个别细节上有所不同：
    void AnySyncQueueDemo();
    AnySyncQueueDemo();

    // 总结：以上介绍的两种条件变量各自有各自的特点：
    //  * condition_variable 配合 unique_lock 使用更灵活一些，可以在在任何时候自由地释放互斥锁；
    //  * 而 condition_variable_any 如果和 lock_guard 一起使用必须要等到其生命周期结束才能将互斥锁释放。
    //  * 但是，condition_variable_any 可以和多种互斥锁配合使用，应用场景也更广，
    //    而 condition_variable 只能和独占的非递归互斥锁（mutex）配合使用，有一定的局限性。

    //*************************************************************************************************************************************************************
    // 原子变量
    // C++11 提供了一个原子类型 std::atomic<T>，通过这个原子类型管理的内部变量就可以称之为原子变量，
    // 我们可以给原子类型指定 bool、char、int、long 指针等类型作为模板参数（不支持浮点类型和复合类型）。
    //
    // 原子指的是一系列不可被 CPU 上下文交换的机器指令，这些指令组合在一起就形成了原子操作。
    // 在多核 CPU 下，当某个 CPU 核心开始运行原子操作时，会先暂停其它 CPU 内核对内存的操作，以保证原子操作不会被其它 CPU 内核所干扰。
    //
    // 由于原子操作是通过指令提供的支持，因此它的性能相比锁和消息传递会好很多。
    // 相比较于锁而言，原子类型不需要开发者处理加锁和释放锁的问题，同时支持修改，读取等操作，还具备较高的并发性能，几乎所有的语言都支持原子类型。
    //
    // 可以看出原子类型是无锁类型，但是无锁不代表无需等待，因为原子类型内部使用了 CAS 循环，当大量的冲突发生时，该等待还是得等待！但是总归是比加锁好一些。
    // 
    // summer: CAS 全称是 Compare And Swap, 它通过一条指令读取指定的内存地址，然后判断其中的值是否等于给定的前置值，如果相等，则将其修改为新的值。
    //         * CAS 不是锁，它是硬件提供得能力，例如：在 Intel 的 CPU 中，使用的是 cmpxchg 指令。因此，CAS 操作能够在硬件层面提升效率。
    //         * CAS 虽然没有锁，但是他是通过不停循环尝试修改，直到修改成功来完成得。顾名思义 “比较并交换”，它是一种无锁原子算法，操作包含三个参数：
    //           - 内存位置 V、预期原值 A 和新值 B。
    //           - 仅当内存位置 V 的值与预期原值 A 相匹配(相等没有变化)时，才会将该位置的值更新为新值 B。
    //           - 如果 V 的值和 A 不同，则说明已经有其他线程做了更新，当前线程得本次操作则什么都不做。CAS 操作的结果会返回 V 的真实值。
    //           - 如果修改失败则会一直尝试 CAS 操作直到成功位置，且此操作不会被终断。显然 CAS 虽然没有锁，但是如果有多线程一起在操作，其中一个线程操作成功其他线程则会循环尝试。
    //             陷入一种变相得等待当中，只是 CAS 没有锁，没有上下文切换，所以性能理论上似乎比加锁更快了，但实际上，CAS 的每次操作并不是无代价的无锁操作，每次操作都会进行比较
    //             和交换内存位置的值（CPU 计算好要把计算后的新值写回变量的内存里）。这些都需要消耗 CPU 资源。所以在某些高并发循环的情况下原子变量并不能表现得很好。
    //           - 从上面了解到，CAS 其实只是对简单基本变量做原子操作，并不能对复杂对象和内存块做原子操作。以为它涉及到了值得比较和交换，复杂对象涉及多个内存块并不适合。
    //             而类似于基本变量中得 long 或 long long 都超过了寻址总线得位数，是一条指令无法完成得修改，并不是原子得所以 CAS 提供了这样得操作来保证这种情况得原子性。
    //         虽然从实现上看 CAS 操作似乎比锁来的更优秀，但实际上它并不能保证所有情况下都要好于加锁操作，甚至是好与单线程无锁操作。
    //         由于 CAS 这种乐观锁的存在，CPU总是乐观的人为修改一定成功。而事实上当大量线程高并发集中处理修改操作的时候它可能由于比较交换的操作开销反而更耗时。
    //         相比之下，使用锁可以避免线程竞争和争用，从而提高系统的整体性能。当一个线程获得锁时，其他线程必须等待该线程释放锁后才能继续执行。
    //         虽然这会导致线程阻塞或挂起，但因为线程可以避免不断循环尝试 CAS 操作所消耗的 CPU 资源。在某些情况下，这可能是更高效的选择。
    //
    // C++11 内置了整形的原子变量，这样就可以更方便的使用原子变量了。在多线程操作中，使用原子变量之后就不需要再使用互斥量来保护该变量了，用起来更简洁。
    // 因为对原子变量进行的操作只能是一个原子操作（atomic operation），原子操作指的是不会被线程调度机制打断的操作，这种操作一旦开始，就一直运行到结束，
    // 中间不会有任何的上下文切换。多线程同时访问共享资源造成数据混乱的原因就是因为 CPU 的上下文切换导致的，使用原子变量解决了这个问题，因此互斥锁的使用也就不再需要了。
    
    // 1. atomic 类成员
    // 类定义于头文件 <atomic>
    //   template<class T>
    //   struct atomic;
    //
    // 通过定义可得知：在使用这个模板类的时候，一定要指定模板类型。
    //
    // 构造函数
    //   构造函数一
    //   atomic() noexcept = default;
    //   构造函数二
    //   constexpr atomic(T desired) noexcept;
    //   构造函数三
    //   atomic(const atomic&) = delete;
    // 构造函数一：默认无参构造函数。
    // 构造函数二：使用 desired 初始化原子变量的值。
    // 构造函数三：使用 =delete 显示删除拷贝构造函数, 不允许进行对象之间的拷贝
    //
    // 公共成员函数
    //  原子类型在类内部重载了=操作符，并且不允许在类的外部使用 =进行对象的拷贝。
    //  T operator=(T desired) noexcept;
    //  T operator=(T desired) volatile noexcept;
    //  
    //  atomic& operator=(const atomic&) = delete;
    //  atomic& operator=(const atomic&) volatile = delete;
    //
    // 原子地以 desired 替换当前值。按照 order 的值影响内存。
    //
    //  void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;
    //  void store(T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept;
    //
    //  * desired：存储到原子变量中的值
    //  * order：强制的内存顺序
    //
    // 原子地加载并返回原子变量的当前值。按照 order 的值影响内存。直接访问原子对象也可以得到原子变量的当前值。
    // T load(std::memory_order order = std::memory_order_seq_cst) const noexcept;
    // T load(std::memory_order order = std::memory_order_seq_cst) const volatile noexcept;
    //
    // 特化成员函数
    // 复合赋值运算符重载，主要包含以下形式：
    //                   T operator+= (T val) volatile noexcept;
    //                   T operator+= (T val) noexcept;
    //                   T operator-= (T val) volatile noexcept;
    //                   T operator-= (T val) noexcept;
    //   模板类型T为整形  T operator&= (T val) volatile noexcept;
    //                   T operator&= (T val) noexcept;
    //                   T operator|= (T val) volatile noexcept;
    //                   T operator|= (T val) noexcept;
    //                   T operator^= (T val) volatile noexcept;
    //                   T operator^= (T val) noexcept; 
    // -----------------------------------------------------------------
    //                   T operator+= (ptrdiff_t val) volatile noexcept;
    //                   T operator+= (ptrdiff_t val) noexcept;
    //   模板类型T为指针  T operator-= (ptrdiff_t val) volatile noexcept;
    //                   T operator-= (ptrdiff_t val) noexcept;
    //
    // 以上各个 operator 都会有对应的 fetch_* 操作，详细见下表：
    // 操作符    操作符重载函数         等级的成员函数    整形  指针  其他
    //   +    atomic::operator+=   atomic::fetch_add    是    是    否
    //   -    atomic::operator-=   atomic::fetch_sub    是    是    否
    //   &    atomic::operator&=   atomic::fetch_and    是    否    否
    //   |    atomic::operator|=   atomic::fetch_or     是    否    否
    //   ^    atomic::operator^=   atomic::fetch_xor    是    否    否
    //
    // 内存顺序约束
    // 通过上面的 API 函数我们可以看出，在调用 atomic 类提供的 API 函数的时候，需要指定原子顺序，
    // 在 C++11 给我们提供的 API 中使用枚举用作执行原子操作的函数的实参，以指定如何同步不同线程上的其他操作。
    //
    // 定义如下:
    //   typedef enum memory_order {
    //       memory_order_relaxed,   // relaxed
    //       memory_order_consume,   // consume
    //       memory_order_acquire,   // acquire
    //       memory_order_release,   // release
    //       memory_order_acq_rel,   // acquire/release
    //       memory_order_seq_cst    // sequentially consistent
    //   } memory_order;
    //
    //  * memory_order_relaxed: 这是最宽松的规则，它对编译器和 CPU 不做任何限制，可以乱序。
    //  * memory_order_release: 释放，设定内存屏障(Memory barrier)，保证它之前的操作永远在它之前，但是它后面的操作可能被重排到它前面
    //  * memory_order_acquire: 获取, 设定内存屏障，保证在它之后的访问永远在它之后，但是它之前的操作却有可能被重排到它后面，往往和 Release 在不同线程中联合使用
    //  * memory_order_consume: 改进版的 memory_order_acquire，开销更小
    //  * memory_order_acq_rel: 它是 Acquire 和 Release 的结合，同时拥有它们俩提供的保证。比如你要对一个 atomic 自增 1，同时希望该操作之前和之后的读取或写入操作不会被重新排序
    //  * memory_order_seq_cst: 顺序一致性，memory_order_seq_cst 就像是 memory_order_acq_rel 的加强版，
    //                          它不管原子操作是属于读取还是写入的操作，只要某个线程有用到 memory_order_seq_cst 的原子操作，
    //                          线程中该 memory_order_seq_cst 操作前的数据操作绝对不会被重新排在该 memory_order_seq_cst 操作之后，
    //                          且该 memory_order_seq_cst 操作后的数据操作也绝对不会被重新排在 memory_order_seq_cst 操作前。

    // 2. 原子变量的使用
    // 假设我们要制作一个多线程交替数数的计数器，我们使用互斥锁和原子变量的方式分别进行实现，对比一下二者的差异：
    
    // 2.1 互斥锁版本
    void MutexCounterDemo();
    MutexCounterDemo();

    // 2.2 原子变量版本
    void AtomicCounterDemo();
    AtomicCounterDemo();

    // 通过代码的对比可以看出，使用了原子变量之后，就不需要再定义互斥量了，在使用上更加简便，并且这两种方式都能保证在多线程操作过程中数据的正确性，不会出现数据的混乱。
    // 原子类型 atomic<T> 可以封装原始数据最终得到一个原子变量对象，操作原子对象能够得到和操作原始数据一样的效果，当然也可以通过 store() 和 load() 来读写原子对象内部的原始数据。

}

mutex g_num_mutex;
int g_num = 0;
void slow_increment(int id)
{
    for (int i = 0; i < 3; ++i)
    {
        // 使用哨兵锁管理互斥锁
        lock_guard<mutex> lock(g_num_mutex);
        ++g_num;
        cout << id << "==>" << g_num << endl;
        this_thread::sleep_for(chrono::seconds(1));
    }
}

timed_mutex g_timed_mutex;
void timed_mutex_work()
{
    chrono::seconds timeout(1);
    while (true)
    {
        // 通过阻塞一定的时长来争取得到互斥锁所有权
        if (g_timed_mutex.try_lock_for(timeout))
        {
            cout << "当前线程ID: " << this_thread::get_id() << ", 得到互斥锁所有权..." << endl;
            // 模拟处理任务用了一定的时长
            this_thread::sleep_for(chrono::seconds(5));
            // 互斥锁解锁
            g_timed_mutex.unlock();
            break;
        }
        else
        {
            cout << "当前线程ID: " << this_thread::get_id() << ", 没有得到互斥锁所有权..." << endl;
            // 模拟处理其他任务用了一定的时长
            this_thread::sleep_for(chrono::milliseconds(50));
        }
    }
}

template <class T>
class SyncQueue
{
public:
    SyncQueue(int maxSize) : mMaxSize(maxSize) {}
    void put(const T &value)
    {
        unique_lock<mutex> locker(mMutex);
        /*
        while (mQueue.size() == mMaxSize) // 检查队列是否满了
        {
            cout << "队列已满，请等待" << endl;
            mNotFull.wait(locker); // 阻塞线程，等待队列被消费后释放
        }
        */
        // condition_variable 的 wait 方法还有一个重载版本，可以接受一个返回值为 bool 的条件函数，条件变量会先检查判断是否满足这个条件:
        // * 如果满足条件（布尔值为 ture），则当前线程重新获得互斥锁的所有权，结束阻塞，继续向下执行；
        // * 如果不满足条件（布尔值为 false），当前线程会释放互斥锁（解锁）同时被阻塞，等待被唤醒。
        // 下面对上面的 wihle 代码进行修改，在 take 函数也做类似修改
        mNotFull.wait(locker, [this]() -> bool {
            return mQueue.size() != mMaxSize;
        });
        // 修改之后可以发现，程序变得更加精简了，而且执行效率更高了，因为在这两个函数中的 while 循环被删掉了，但是最终的效果是一样的，推荐使用这种方式的 wait() 进行线程的阻塞。
        // summer: 上面的这段话来自某网络教程，对于精简是确实精简了，但是对于执行效率更高持保留意见，
        //         因为实际上第二个条件函数版本，其内部实现也是通过 while 完成的，这并没有提升性能，反而对于 lamuda 的使用和函数回调的开销似乎更大了。
        // 附上 wait 的条件函数版本：
        //      template<typename _Predicate>
        //      void wait(unique_lock<mutex>& __lock, _Predicate __p)
        //      {
        //          while (!__p())
        //              wait(__lock);
        //      }
    
        mQueue.push_back(value); // 队列有空位则存入队列中
        cout << value << "被生产" << endl;
        mNotEmpty.notify_one(); // 然后通知消费
    }

    T take()
    {
        unique_lock<mutex> locker(mMutex);
        /*
        while (mQueue.empty()) // 检查队列是否为空
        {
            cout << "队列是空的，请等待" << endl;
            mNotEmpty.wait(locker); // 阻塞线程，等待生产
        }
        */
        // 使用条件函数判定替代while循环
        mNotEmpty.wait(locker, [this]() -> bool {
            return !mQueue.empty();
        });
        T value = mQueue.front(); // 从队列中取出
        mQueue.pop_front();
        cout << value << "被消费了" << endl;
        mNotFull.notify_one(); // 然后通知生产
        return value;
    }

    bool empty()
    {
        lock_guard<mutex> locker(mMutex);
        return mQueue.empty();
    }

    bool full()
    {
        lock_guard<mutex> locker(mMutex);
        return mQueue.size() == mMaxSize;
    }

    int size()
    {
        lock_guard<mutex> locker(mMutex);
        return mQueue.size();
    }

private:
    // 存储队列大小
    int mMaxSize;
    // 存储互斥队列
    list<T> mQueue;
    // 独占互斥锁
    mutex mMutex;
    // 不为空的条件变量
    condition_variable mNotEmpty;
    // 不为满的条件变量
    condition_variable mNotFull;
};

void syncQueueDemo()
{
    cout << "演示使用 condition_variable 完成的生产者消费者模型" << endl;
    SyncQueue<int> taskQueue(10);

    auto produce = bind(&SyncQueue<int>::put, &taskQueue, placeholders::_1);
    auto consume = bind(&SyncQueue<int>::take, &taskQueue);
    thread produceThreads[3];
    thread consumeThreads[3];
    for (int i = 0; i < 3; ++i)
    {
        produceThreads[i] = thread(produce, i + 100);
        consumeThreads[i] = thread(consume);
    }
    for (int i = 0; i < 3; ++i)
    {
        produceThreads[i].join();
        consumeThreads[i].join();
    }
}

template <class T>
class AnySyncQueue 
{
public:
    AnySyncQueue(int maxSize): mMaxSize(maxSize) {}
    void put(const T &value)
    {
        // 构造函数会对 mMutex 加锁，而析构函数会自动解锁
        lock_guard<mutex> locker(mMutex);
        // 如果 wait 成立被阻塞，则会释放 mMutex 的锁，如果被重新唤起则会重新争夺锁的使用权。
        mNotFull.wait(mMutex, [this]() -> bool {
            return mQueue.size() != mMaxSize;
        });
        mQueue.push_back(value);
        cout << value << "被生产了" << endl;
        mNotEmpty.notify_one();
    }

    T take()
    {
        // 构造函数会对 mMutex 加锁，而析构函数会自动解锁
        lock_guard<mutex> locker(mMutex);
        // 如果 wait 成立被阻塞，则会释放 mMutex 的锁，如果被重新唤起则会重新争夺锁的使用权。
        mNotEmpty.wait(mMutex, [this]() -> bool {
            return !mQueue.empty();
        });
        T value = mQueue.front();
        mQueue.pop_front();
        cout << value << "被消费了" << endl;
        mNotFull.notify_one();
        return value;
    }

    bool empty()
    {
        lock_guard<mutex> locker(mMutex);
        return mQueue.empty();
    }

    bool full()
    {
        lock_guard<mutex> locker(mMutex);
        return mQueue.size() == mMaxSize;
    }

    int size()
    {
        lock_guard<mutex> locker(mMutex);
        return mQueue.size();
    }

private:
    // 存储队列大小
    int mMaxSize;
    // 存储互斥队列
    list<T> mQueue;
    // 独占互斥锁
    mutex mMutex;
    // 不为空的条件变量
    condition_variable_any mNotEmpty;
    // 不为满的条件变量
    condition_variable_any mNotFull;
};

void AnySyncQueueDemo() 
{
    cout << "演示使用 condition_variable_any 完成的生产者消费者模型" << endl;
    AnySyncQueue<int> syncQueue(10);
    auto produce = bind(&AnySyncQueue<int>::put, &syncQueue, placeholders::_1);
    auto consume = bind(&AnySyncQueue<int>::take, &syncQueue);
    thread t1[3];
    thread t2[3];
    for (int i = 0; i < 3; ++i)
    {
        t1[i] = thread(produce, i + 100);
        t2[i] = thread(consume);
    }

    for (int i = 0; i < 3; ++i)
    {
        t1[i].join();
        t2[i].join();
    }
}

class MutexCounter
{
public:
    void increment()
    {
        for(int i = 0; i < 10; ++i)
        {
            lock_guard<mutex> locker(mMutex);
            mValue++;
            cout << "MutexCounter increment number::" << mValue << "; thread id:" << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    }
    void decrement()
    {
        for(int i = 0; i < 10; ++i)
        {
            lock_guard<mutex> locker(mMutex);
            mValue--;
            cout << "MutexCounter decrement number::" << mValue << "; thread id：" << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    }
private:
    int mValue = 0;
    mutex mMutex;
};

void MutexCounterDemo() 
{
    MutexCounter counter;
    auto increment = bind(&MutexCounter::increment, & counter);
    auto decrement = bind(&MutexCounter::decrement, & counter);
    thread t1(increment);    
    thread t2(decrement);
    t1.join();
    t2.join();
}

class AtomicCounter
{
public:
    void increment()
    {
        stringstream msgStream;
        for(int i = 0; i < 10; ++i)
        {
            mValue++;
            msgStream.clear();
            msgStream << "AtomicCounter increment number::" << mValue << "; thread id:" << this_thread::get_id() << endl;
            cout << msgStream.str();
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    }
    void decrement()
    {
        stringstream msgStream;
        for(int i = 0; i < 10; ++i)
        {
            mValue--;
            msgStream.clear();
            msgStream << "AtomicCounter decrement number::" << mValue << "; thread id:" << this_thread::get_id() << endl;
            cout << msgStream.str();
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    }
private:
    // atomic<int> mValue;
    atomic_int mValue {0};
};

void AtomicCounterDemo()
{
    AtomicCounter counter;
    auto increment = bind(&AtomicCounter::increment, &counter);
    auto decrement = bind(&AtomicCounter::decrement, &counter);
    thread t1(increment);
    thread t2(decrement);
    t1.join();    
    t2.join();
}